package hu.szte.ovrt.mzst;

import hu.szte.ovrt.mzst.exceptions.BadOperandException;
import hu.szte.ovrt.mzst.exceptions.InvalidOperationException;
import hu.szte.ovrt.mzst.exceptions.InvalidSyntaxException;
import hu.szte.ovrt.mzst.operations.Copy;
import hu.szte.ovrt.mzst.operations.CreateDirectory;
import hu.szte.ovrt.mzst.operations.CreateFile;
import hu.szte.ovrt.mzst.operations.DeleteDirectory;
import hu.szte.ovrt.mzst.operations.DeleteFile;
import hu.szte.ovrt.mzst.operations.Move;
import hu.szte.ovrt.mzst.operations.OneOperandOperation;
import hu.szte.ovrt.mzst.operations.TwoOperandOperation;
import hu.szte.ovrt.visitors.PrintExpressionVisitor;

import java.util.Iterator;
import java.util.List;
import java.util.Scanner;

public class Parser {
	
	private Block actualBlock;
		
	/**
	 * @param tokens Egy (ENTER-rel zarulo) sor elemei. 
	 * A veges szokoznel tortenik, specialis eset a kapcsoszarojel, az kulon token lesz. 
	 * Szokozoket nem tartalmazhat egy token sem. 
	 * @return 
	 * @throws InvalidSyntaxException 
	 * @throws InvalidOperationException 
	 * @throws BadOperandException 
	 * @throws AlreadyExistsException 
	 */
	public boolean start(Scanner scanIn, List<String> tokens) throws InvalidSyntaxException, InvalidOperationException, BadOperandException {
		
		for (Iterator<String> iterator = tokens.iterator(); iterator.hasNext();) {
			String token = (String) iterator.next();
			
			if (token.equals(Literal.BLOCK_START)){
				if (actualBlock == null){
					actualBlock = new Block(null);
				} else {
					Block newBlock = new Block(actualBlock);
					actualBlock = newBlock;
				}
			} else if (token.equals(Literal.BLOCK_END)){
				if (actualBlock == null){
					throw new InvalidSyntaxException();
				} else if (actualBlock.getParentBlock() == null){
					//actualBlock.interpret();
					blockHandler(scanIn, actualBlock);
					actualBlock = null;
					return true;
				} else {
					actualBlock.getParentBlock().addExpression(actualBlock);
					actualBlock = actualBlock.getParentBlock();
				}
			} else if (Literal.OPERATIONS.contains(token)){
				OneOperandOperation operation = null;	
				String firstOperand = getOperand(iterator);
				if (Literal.ONE_OPERAND_OPERATIONS.contains(token)){
					operation = createOneOperandOperation(token, firstOperand);
				} else if (Literal.TWO_OPERAND_OPERATIONS.contains(token)){
					String secondOperand = getOperand(iterator);
					operation = createTwoOperandOperation(token, firstOperand, secondOperand);
				} 
				
				if (actualBlock == null){
					operation.execute();
					return true;
				} else {
					actualBlock.addExpression(operation);
				}
				
			} else {
				throw new InvalidOperationException();
			}
		}
		return false;
	}
	
	private String getOperand(Iterator<String> iterator) throws InvalidSyntaxException{
		if (iterator.hasNext()){
			String nextElement = iterator.next();
			if (!Literal.ALL_KEYWORD.contains(nextElement))
				return nextElement;		
		}
		
		throw new InvalidSyntaxException();
	}
	
	private OneOperandOperation createOneOperandOperation(String token, String firstOperand){
		OneOperandOperation oneOperandOperation = null;
		if (token.equals(Literal.CREATE_FILE)){
			oneOperandOperation = new CreateFile(firstOperand);
		} else if (token.equals(Literal.CREATE_DIRECTORY)){
			oneOperandOperation = new CreateDirectory(firstOperand);
		} else if (token.equals(Literal.DELETE_FILE)){
			oneOperandOperation = new DeleteFile(firstOperand);
		} else if (token.equals(Literal.DELETE_DIRECTORY)){
			oneOperandOperation = new DeleteDirectory(firstOperand);
		}
		
		return oneOperandOperation;
	}
	
	private TwoOperandOperation createTwoOperandOperation(String token, String firstOperand, String secondOperand){
		TwoOperandOperation twoOperandOperation = null;
		if (token.equals(Literal.COPY)){
			twoOperandOperation = new Copy(firstOperand, secondOperand);
		} else if (token.equals(Literal.MOVE)){
			twoOperandOperation = new Move(firstOperand, secondOperand);
		} 
		
		return twoOperandOperation;
	}
	
	public void clear() {
		actualBlock = null;
	}
	
	private void blockHandler(Scanner scanIn, Block actualBlock) throws BadOperandException{	
		System.out.println(Literal.BLOCK_EXEC_CONFIRM);
		actualBlock.accept(new PrintExpressionVisitor());
		
		String input = scanIn.nextLine().toLowerCase();
		
		if (input.equals(Literal.YES)){
			actualBlock.interpret();
		} else if (!input.equals(Literal.NO)){
			System.out.println(Literal.INVALID_YES_OR_NO + "\n");
		}
	}
	
	
}
